package com.perceptivesoftware.licensing.client;

import java.util.Date;
import java.util.List;
import java.util.Random;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MessageBoxEvent;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.perceptivesoftware.licensing.client.common.util.ClientConstants;
import com.perceptivesoftware.licensing.client.common.util.LogoffTimer;
import com.perceptivesoftware.licensing.client.common.util.NPAction;
import com.perceptivesoftware.licensing.client.common.util.NPType;
import com.perceptivesoftware.licensing.client.common.util.PerceptivePermission;
import com.perceptivesoftware.licensing.client.common.util.Utils;
import com.perceptivesoftware.licensing.client.common.util.LogoffTimer.LogoffHandler;
import com.perceptivesoftware.licensing.client.dialogs.useradmin.callbackhandlers.SecurityManagerAsyncCallback;
import com.perceptivesoftware.licensing.client.dialogs.useradmin.plugin.role.RoleNavItem;
import com.perceptivesoftware.licensing.client.dialogs.useradmin.plugin.user.UserNavItem;
import com.perceptivesoftware.licensing.client.dto.SecurityTreeDTO;
import com.perceptivesoftware.licensing.client.dto.UserDTO;
import com.perceptivesoftware.licensing.client.dto.UserInitialDTO;

/**
 * Client side session containing prefetched data from the server: user, role, workspace, version, permissions.
 * 
 * Copyright 2012 Perceptive Software Project: Perceptive Licensing
 * 
 * @author Laurie.Katz
 * 
 */
public class PerceptiveUISession
{

	// version information
	private static PerceptiveUISession session;
	private final LogoffTimer logoffTimer;
	private SecurityTreeDTO securityTreeDTO;
	private UserInitialDTO userInitialDTO;
	private HeartbeatTimer heartbeatTimer;
	private HandlerRegistration[] windowCloseRegistrations;

	private PerceptiveUISession()
	{
		super();
		logoffTimer = new LogoffTimer(new LogoffHandler() {

			@Override
			public void logoff(final boolean timeout)
			{
				requestLogoff(timeout);
			}
		});
		// setLicenseDialogDTO();
	}

	public static PerceptiveUISession getInstance()
	{
		if (session == null)
		{
			session = new PerceptiveUISession();
		}
		return session;
	}

	public List<RoleNavItem> getInitialRoles()
	{
		return securityTreeDTO.getRoles();
	}

	public void setUserInitialDTO(final UserInitialDTO userInitialDTO)
	{
		this.userInitialDTO = userInitialDTO;
	}

	public String getBuildVersionInfo()
	{
		return userInitialDTO.getVersionInformation();
	}

	private void performLogOff(final boolean isTimeout, final String redirectURL)
	{
		LicensingRPCFactory.getSessionService(true).logoff(isTimeout, new LicensingAsyncCallback<Void>() {

			@Override
			public void onSuccess(@SuppressWarnings("unused") final Void result)
			{
				redirectToPage(redirectURL);
			}

			@Override
			public void onFailure(@SuppressWarnings("unused") Throwable caught)
			{
				MessageBox.alert("Logoff", "Perceptive Licensing was unable to successfully log off.", new Listener<MessageBoxEvent>() {

					@Override
					public void handleEvent(@SuppressWarnings("unused") MessageBoxEvent be)
					{
						redirectToPage(redirectURL);
					}
				});
			}

		});
	}

	protected void redirectToLogoffPage()
	{
		redirectToPage(ClientConstants.LOGOUT_URL);
	}

	private void redirectToPage(final String logoutUrl)
	{
		removeWindowCloseListener();
		cancelHeartbeatTimer();
		Utils.redirectToUrl(logoutUrl + Window.Location.getQueryString());
	}

	void requestLogoff(final boolean timeout)
	{
		performLogOff(timeout, ClientConstants.LOGOUT_URL);
	}

	public void configureSessionTimer(final Long timeoutMins)
	{
		logoffTimer.configureTimeout(timeoutMins);
	}

	private void resetLogoff()
	{
		logoffTimer.reset();
	}

	void startHeartbeatTimer()
	{
		heartbeatTimer = new HeartbeatTimer();
		heartbeatTimer.schedule(ClientConstants.HEARTBEAT_SECONDS * 1000);
	}

	private class HeartbeatTimer extends Timer
	{

		@Override
		public void run()
		{
			final Date today = new Date();
			final Random generator = new Random();
			final int hbCode = generator.nextInt();
			GWT.log(DateTimeFormat.getMediumDateTimeFormat().format(today) + " - Heartbeat " + hbCode + " : RPC to server.");
			LicensingRPCFactory.getSessionService(false).heartbeat(session.getSessionID(), new SecurityManagerAsyncCallback<String>() {

				@Override
				public void onSuccess(@SuppressWarnings("unused") final String errorMessage)
				{
					if (errorMessage != null)
					{
						Window.alert(errorMessage);
						removeWindowCloseListener();
						Utils.redirectToUrl(ClientConstants.USER_LOGIN_URL + Window.Location.getQueryString());
						return;
					}
					// For debugging
					final Date today = new Date();
					GWT.log(DateTimeFormat.getMediumDateTimeFormat().format(today) + " - Heartbeat " + hbCode + " : successful return from server, scheduling next one.");

					schedule(ClientConstants.HEARTBEAT_SECONDS * 1000);
				}

				@Override
				public void onPerceptiveFailure(final Throwable t)
				{
					// For debugging
					final Date today = new Date();
					GWT.log(DateTimeFormat.getMediumDateTimeFormat().format(today) + " - Heartbeat " + hbCode + " : failure return from server");

					Window.alert(t.getMessage());
					removeWindowCloseListener();
					Utils.redirectToUrl(ClientConstants.USER_LOGIN_URL + Window.Location.getQueryString());
					return;
				}
			});
		}
	}

	public boolean ntUservalDTOIsNull()
	{
		return (userInitialDTO == null);
	}

	public Boolean hasPermission(final PerceptivePermission np)
	{
		if (isSuperAdmin())
		{
			return true;
		}
		else
		{
			return userInitialDTO.hasPermission(np);
		}
	}

	public Boolean hasPermission(final NPType type, final NPAction action)
	{
		if (isSuperAdmin())
		{
			return true;
		}
		else
		{
			return userInitialDTO.hasPermission(new PerceptivePermission(type, action));
		}
	}

	private void cancelHeartbeatTimer()
	{
		if (heartbeatTimer != null)
		{
			heartbeatTimer.cancel();
		}
	}

	private void removeWindowCloseListener()
	{
		if (windowCloseRegistrations == null)
		{
			return;
		}
		for (final HandlerRegistration hr : windowCloseRegistrations)
		{
			hr.removeHandler();
		}
	}

	void setWindowCloseListenerRegistrations(final HandlerRegistration windowClosingHandler, final HandlerRegistration closeHandler)
	{
		windowCloseRegistrations = new HandlerRegistration[]
		{ windowClosingHandler, closeHandler };
	}

	public boolean isSuperAdmin()
	{
		return userInitialDTO.getUserDTO().isSuperUser();
	}

	public UserDTO getUserDTO()
	{
		return userInitialDTO.getUserDTO();
	}

	private final String db1Name = null;

	public String getDb1Name()
	{
		return db1Name;
	}

	static void resetLogOff()
	{
		if (getInstance() != null)
		{
			getInstance().resetLogoff();
		}
	}

	public SecurityTreeDTO getSecurityTreeDTO()
	{
		return securityTreeDTO;
	}

	public void setSecurityTreeDTO(final SecurityTreeDTO securityTreeDTO)
	{
		this.securityTreeDTO = securityTreeDTO;
	}

	public List<UserNavItem> getAllUsers()
	{
		return securityTreeDTO.getUsers();
	}

	public String getSessionID()
	{
		return userInitialDTO.getUSessionID();
	}
}